/*********************************************************************
 *  The following code is part of the power modelling feature of VTR.
 *
 * For support:
 * http://code.google.com/p/vtr-verilog-to-routing/wiki/Power
 *
 * or email:
 * vtr.power.estimation@gmail.com
 *
 * If you are using power estimation for your researach please cite:
 *
 * Jeffrey Goeders and Steven Wilton.  VersaPower: Power Estimation
 * for Diverse FPGA Architectures.  In International Conference on
 * Field Programmable Technology, 2012.
 *
 ********************************************************************/

/**
 * The functions in this file are used to count the transistors in the
 * FPGA, for physical size estimations
 */

/************************* INCLUDES *********************************/
#include <cstring>
#include <cmath>

#include "vtr_util.h"
#include "vtr_assert.h"
#include "vtr_memory.h"

#include "power_sizing.h"
#include "power.h"
#include "globals.h"
#include "power_util.h"
#include "vpr_utils.h"
/************************ FILE SCOPE *********************************/
static double f_MTA_area;

/************************* FUNCTION DECLARATIONS ********************/
static double power_count_transistors_connectionbox();
static double power_count_transistors_mux(t_mux_arch* mux_arch);
static double power_count_transistors_mux_node(t_mux_node* mux_node,
                                               float transistor_size);
static void power_mux_node_max_inputs(t_mux_node* mux_node,
                                      float* max_inputs);
static double power_count_transistors_interc(t_interconnect* interc);
static double power_count_transistors_pb_node(t_pb_graph_node* pb_node);
static double power_count_transistors_switchbox();
static double power_count_transistors_primitive(t_pb_type* pb_type);
static double power_count_transistors_LUT(int LUT_inputs,
                                          float transistor_size);
static double power_count_transistors_FF(float size);
static double power_count_transistor_SRAM_bit();
static double power_count_transistors_inv(float size);
static double power_count_transistors_trans_gate(float size);
static double power_count_transistors_levr();
static void power_size_pin_buffers_and_wires(t_pb_graph_pin* pin,
                                             bool pin_is_an_input);
static double power_transistors_for_pb_node(t_pb_graph_node* pb_node);
static double power_transistors_per_tile();
static void power_size_pb();
static void power_size_pb_rec(t_pb_graph_node* pb_node);
static void power_size_pin_to_interconnect(t_interconnect* interc,
                                           int* fanout,
                                           float* wirelength);
static double power_MTAs(float W_size);
static double power_MTAs_L(float L_size);
/************************* FUNCTION DEFINITIONS *********************/

/**
 *  Counts the number of transistors in the largest connection box
 */
static double power_count_transistors_connectionbox() {
    double transistor_cnt = 0.;
    int inputs;
    float buffer_size;

    auto& device_ctx = g_vpr_ctx.device();
    auto& power_ctx = g_vpr_ctx.power();

    auto type = find_most_common_block_type(device_ctx.grid);
    VTR_ASSERT(type->pb_graph_head->num_input_ports == 1);
    inputs = type->pb_graph_head->num_input_pins[0];

    /* Buffers from Tracks */
    buffer_size = power_ctx.commonly_used->max_seg_to_IPIN_fanout
                  * (power_ctx.commonly_used->NMOS_1X_C_d
                     / power_ctx.commonly_used->INV_1X_C_in)
                  / power_ctx.arch->logical_effort_factor;
    buffer_size = std::max(1.0F, buffer_size);
    transistor_cnt += power_ctx.solution_inf.channel_width
                      * power_count_transistors_buffer(buffer_size);

    /* Muxes to IPINs */
    transistor_cnt += inputs
                      * power_count_transistors_mux(
                            power_get_mux_arch(power_ctx.commonly_used->max_IPIN_fanin,
                                               power_ctx.arch->mux_transistor_size));

    return transistor_cnt;
}

/**
 * Counts the number of transistors in a buffer.
 * - buffer_size: The final stage size of the buffer, relative to a minimum sized inverter
 */
double power_count_transistors_buffer(float buffer_size) {
    int stages;
    float effort;
    float stage_size;
    int stage_idx;
    double transistor_cnt = 0.;

    auto& power_ctx = g_vpr_ctx.power();

    stages = power_calc_buffer_num_stages(buffer_size,
                                          power_ctx.arch->logical_effort_factor);
    effort = calc_buffer_stage_effort(stages, buffer_size);

    stage_size = 1;
    for (stage_idx = 0; stage_idx < stages; stage_idx++) {
        transistor_cnt += power_count_transistors_inv(stage_size);
        stage_size *= effort;
    }

    return transistor_cnt;
}

/**
 * Counts the number of transistors in a multiplexer
 * - mux_arch: The architecture of the multiplexer
 */
static double power_count_transistors_mux(t_mux_arch* mux_arch) {
    double transistor_cnt = 0.;
    int lvl_idx;
    float* max_inputs;

    /* SRAM bits */
    max_inputs = (float*)vtr::calloc(mux_arch->levels, sizeof(float));
    for (lvl_idx = 0; lvl_idx < mux_arch->levels; lvl_idx++) {
        max_inputs[lvl_idx] = 0.;
    }
    power_mux_node_max_inputs(mux_arch->mux_graph_head, max_inputs);

    for (lvl_idx = 0; lvl_idx < mux_arch->levels; lvl_idx++) {
        /* Assume there is decoder logic */
        transistor_cnt += ceil(log(max_inputs[lvl_idx]) / log((double)2.0))
                          * power_count_transistor_SRAM_bit();

        /*
         * if (mux_arch->encoding_types[lvl_idx] == ENCODING_DECODER) {
         * transistor_cnt += ceil(log2((float)max_inputs[lvl_idx]))
         * power_cnt_transistor_SRAM_bit();
         * // TODO - Size of decoder
         * } else if (mux_arch->encoding_types[lvl_idx] == ENCODING_ONE_HOT) {
         * transistor_cnt += max_inputs[lvl_idx]
         * power_cnt_transistor_SRAM_bit();
         * } else {
         * VTR_ASSERT(0);
         * }
         */
    }

    transistor_cnt += power_count_transistors_mux_node(mux_arch->mux_graph_head,
                                                       mux_arch->transistor_size);
    free(max_inputs);
    return transistor_cnt;
}

/**
 *  This function is used recursively to determine the largest single-level
 *  multiplexer at each level of a multi-level multiplexer
 */
static void power_mux_node_max_inputs(t_mux_node* mux_node,
                                      float* max_inputs) {
    max_inputs[mux_node->level] = std::max(max_inputs[mux_node->level],
                                           static_cast<float>(mux_node->num_inputs));

    if (mux_node->level != 0) {
        int child_idx;

        for (child_idx = 0; child_idx < mux_node->num_inputs; child_idx++) {
            power_mux_node_max_inputs(&mux_node->children[child_idx],
                                      max_inputs);
        }
    }
}

/**
 * This function is used recursively to count the number of transistors in a multiplexer
 */
static double power_count_transistors_mux_node(t_mux_node* mux_node,
                                               float transistor_size) {
    int input_idx;
    double transistor_cnt = 0.;

    if (mux_node->num_inputs != 1) {
        for (input_idx = 0; input_idx < mux_node->num_inputs; input_idx++) {
            /* Single Pass transistor */
            transistor_cnt += power_MTAs(transistor_size);

            /* Child MUX */
            if (mux_node->level != 0) {
                transistor_cnt += power_count_transistors_mux_node(&mux_node->children[input_idx], transistor_size);
            }
        }
    }

    return transistor_cnt;
}

/**
 * This function returns the number of transistors in an interconnect structure
 */
static double power_count_transistors_interc(t_interconnect* interc) {
    double transistor_cnt = 0.;

    switch (interc->type) {
        case DIRECT_INTERC:
            /* No transistors */
            break;
        case MUX_INTERC:
        case COMPLETE_INTERC: {
            /* Bus based interconnect:
             * - Each output port requires a (num_input_ports:1) bus-based multiplexor.
             * - The number of muxes required for bus based multiplexors is equivalent to
             * the width of the bus (num_pins_per_port).
             */
            auto& power_ctx = g_vpr_ctx.power();
            transistor_cnt += interc->interconnect_power->num_output_ports
                              * interc->interconnect_power->num_pins_per_port
                              * power_count_transistors_mux(
                                    power_get_mux_arch(
                                        interc->interconnect_power->num_input_ports,
                                        power_ctx.arch->mux_transistor_size));
            break;
        }
        default:
            VTR_ASSERT(0);
    }

    interc->interconnect_power->transistor_cnt = transistor_cnt;
    return transistor_cnt;
}

void power_sizing_init() {
    float transistors_per_tile;

    auto& power_ctx = g_vpr_ctx.power();

    // tech size = 2 lambda, so lambda^2/4.0 = tech^2
    f_MTA_area = ((POWER_MTA_L * POWER_MTA_W) / 4.0) * pow(power_ctx.tech->tech_size, (float)2.0);

    // Determines physical size of different PBs
    power_size_pb();

    /* Find # of transistors in each tile type */
    transistors_per_tile = power_transistors_per_tile();

    /* Calculate CLB tile size
     *  - Assume a square tile
     *  - Assume min transistor size is Wx6L
     *  - Assume an overhead to space transistors
     */
    power_ctx.commonly_used->tile_length = sqrt(power_transistor_area(transistors_per_tile));
}

/**
 * This functions counts the number of transistors in all structures in the FPGA.
 * It returns the number of transistors in a grid of the FPGA (logic block,
 * switch box, 2 connection boxes)
 */
static double power_transistors_per_tile() {
    auto& device_ctx = g_vpr_ctx.device();

    double transistor_cnt = 0.;

    auto type = find_most_common_block_type(device_ctx.grid);
    transistor_cnt += power_transistors_for_pb_node(type->pb_graph_head);

    transistor_cnt += 2 * power_count_transistors_switchbox();

    transistor_cnt += 2 * power_count_transistors_connectionbox();

    return transistor_cnt;
}

static double power_transistors_for_pb_node(t_pb_graph_node* pb_node) {
    return pb_node->pb_node_power->transistor_cnt_interc
           + pb_node->pb_node_power->transistor_cnt_pb_children
           + pb_node->pb_node_power->transistor_cnt_buffers;
}

/**
 * This function counts the number of transistors for a given physical block type
 */
static double power_count_transistors_pb_node(t_pb_graph_node* pb_node) {
    int mode_idx;
    int interc;
    int child;
    int pb_idx;

    double tc_children_max = 0;
    double tc_interc_max = 0;
    bool ignore_interc = false;

    t_pb_type* pb_type = pb_node->pb_type;

    /* Check if this is a leaf node, or whether it has children */
    if (pb_type->num_modes == 0) {
        /* Leaf node */
        tc_interc_max = 0;
        tc_children_max = power_count_transistors_primitive(pb_type);
    } else {
        /* Find max transistor count between all modes */
        for (mode_idx = 0; mode_idx < pb_type->num_modes; mode_idx++) {
            double tc_children = 0;
            double tc_interc = 0;

            t_mode* mode = &pb_type->modes[mode_idx];

            if (pb_type->class_type == LUT_CLASS) {
                /* LUTs will have a child node that is used for routing purposes
                 * For the routing algorithms it is completely connected; however,
                 * this interconnect does not exist in FPGA hardware and should
                 * be ignored for power calculations. */
                ignore_interc = true;
            }

            /* Count Interconnect Transistors */
            if (!ignore_interc) {
                for (interc = 0; interc < mode->num_interconnect; interc++) {
                    tc_interc += power_count_transistors_interc(&mode->interconnect[interc]);
                }
            }
            tc_interc_max = std::max(tc_interc_max, tc_interc);

            /* Count Child PB Types */
            for (child = 0; child < mode->num_pb_type_children; child++) {
                t_pb_type* child_type = &mode->pb_type_children[child];

                for (pb_idx = 0; pb_idx < child_type->num_pb; pb_idx++) {
                    tc_children += power_transistors_for_pb_node(&pb_node->child_pb_graph_nodes[mode_idx][child][pb_idx]);
                }
            }

            tc_children_max = std::max(tc_children_max, tc_children);
        }
    }

    pb_node->pb_node_power->transistor_cnt_interc = tc_interc_max;
    pb_node->pb_node_power->transistor_cnt_pb_children = tc_children_max;

    return (tc_interc_max + tc_children_max);
}

/**
 * This function counts the maximum number of transistors in a switch box
 */
static double power_count_transistors_switchbox() {
    double transistor_cnt = 0.;
    double transistors_per_buf_mux = 0.;

    auto& power_ctx = g_vpr_ctx.power();

    /* Buffer */
    transistors_per_buf_mux += power_count_transistors_buffer(
        (float)power_ctx.commonly_used->max_seg_fanout
        / power_ctx.arch->logical_effort_factor);

    /* Multiplexor */
    transistors_per_buf_mux += power_count_transistors_mux(
        power_get_mux_arch(power_ctx.commonly_used->max_routing_mux_size,
                           power_ctx.arch->mux_transistor_size));

    auto& device_ctx = g_vpr_ctx.device();

    for (size_t seg_idx = 0; seg_idx < device_ctx.rr_segments.size(); seg_idx++) {
        /* In each switchbox, the different types of segments occur with relative freqencies.
         * Thus the total number of wires of each segment type is (#tracks * freq * 2).
         * The (x2) factor accounts for vertical and horizontal tracks.
         * Of the wires of each segment type only (1/seglength) will have a mux&buffer.
         */
        float freq_frac = (float)device_ctx.rr_segments[seg_idx].frequency
                          / (float)MAX_CHANNEL_WIDTH;

        transistor_cnt += transistors_per_buf_mux * 2 * freq_frac
                          * power_ctx.solution_inf.channel_width
                          * (1 / (float)device_ctx.rr_segments[seg_idx].length);
    }

    return transistor_cnt;
}

/**
 * This function calculates the number of transistors for a primitive physical block
 */
static double power_count_transistors_primitive(t_pb_type* pb_type) {
    double transistor_cnt;

    auto& power_ctx = g_vpr_ctx.power();

    if (strcmp(pb_type->blif_model, MODEL_NAMES) == 0) {
        /* LUT */
        transistor_cnt = power_count_transistors_LUT(pb_type->num_input_pins,
                                                     power_ctx.arch->LUT_transistor_size);
    } else if (strcmp(pb_type->blif_model, MODEL_LATCH) == 0) {
        /* Latch */
        transistor_cnt = power_count_transistors_FF(power_ctx.arch->FF_size);
    } else {
        /* Other */
        char msg[vtr::bufsize];

        sprintf(msg, "No transistor counter function for BLIF model: %s",
                pb_type->blif_model);
        power_log_msg(POWER_LOG_WARNING, msg);
        transistor_cnt = 0;
    }

    return transistor_cnt;
}

/**
 * Returns the transistor count of an SRAM cell
 */
static double power_count_transistor_SRAM_bit() {
    auto& power_ctx = g_vpr_ctx.power();
    return power_ctx.arch->transistors_per_SRAM_bit;
}

static double power_count_transistors_levr() {
    double transistor_cnt = 0.;

    /* Each level restorer has a P/N=1/2 inverter and a W/L=1/2 PMOS */

    /* Inverter */
    transistor_cnt += power_MTAs(2); // NMOS
    transistor_cnt += 1.0;           // PMOS

    /* Pull-up */
    transistor_cnt += power_MTAs_L(2.0); // Double length

    return transistor_cnt;
}

/**
 * Returns the transistor count for a LUT
 */
static double power_count_transistors_LUT(int LUT_inputs,
                                          float transistor_size) {
    double transistor_cnt = 0.;
    int level_idx;

    /* Each input driver has 1-1X and 2-2X inverters */
    transistor_cnt += (double)LUT_inputs
                      * (power_count_transistors_inv(1.0)
                         + 2 * power_count_transistors_inv(2.0));

    /* SRAM bits */
    transistor_cnt += power_count_transistor_SRAM_bit() * (1 << LUT_inputs);

    for (level_idx = 0; level_idx < LUT_inputs; level_idx++) {
        /* Pass transistors */
        transistor_cnt += (1 << (LUT_inputs - level_idx))
                          * power_MTAs(transistor_size);

        /* Add level restorer after every 2 stages (level_idx %2 == 1)
         * But if there is an odd # of stages, just put one at the last
         * stage (level_idx == LUT_size - 1) and not at the stage just before
         * the last stage (level_idx != LUT_size - 2)
         */
        if (((level_idx % 2 == 1) && (level_idx != LUT_inputs - 2))
            || (level_idx == LUT_inputs - 1)) {
            transistor_cnt += power_count_transistors_levr();
        }
    }

    return transistor_cnt;
}

static double power_count_transistors_trans_gate(float size) {
    double transistor_cnt = 0.;
    auto& power_ctx = g_vpr_ctx.power();

    transistor_cnt += power_MTAs(size);
    transistor_cnt += power_MTAs(size * power_ctx.tech->PN_ratio);

    return transistor_cnt;
}

static double power_count_transistors_inv(float size) {
    double transistor_cnt = 0.;
    auto& power_ctx = g_vpr_ctx.power();

    /* NMOS */
    transistor_cnt += power_MTAs(size);

    /* PMOS */
    transistor_cnt += power_MTAs(power_ctx.tech->PN_ratio * size);

    return transistor_cnt;
}

/**
 * Returns the transistor count for a flip-flop
 */
static double power_count_transistors_FF(float size) {
    double transistor_cnt = 0.;

    /* 4 1X Inverters */
    transistor_cnt += 4 * power_count_transistors_inv(size);

    /* 2 Muxes = 4 transmission gates */
    transistor_cnt += 4 * power_count_transistors_trans_gate(size);

    return transistor_cnt;
}

double power_transistor_area(double num_MTAs) {
    return num_MTAs * f_MTA_area;
}

static double power_MTAs(float W_size) {
    return 1 + (W_size - 1) * (POWER_DRC_MIN_W / POWER_MTA_W);
}

static double power_MTAs_L(float L_size) {
    return 1 + (L_size - 1) * (POWER_DRC_MIN_L / POWER_MTA_L);
}

static void power_size_pb() {
    auto& device_ctx = g_vpr_ctx.device();

    for (const auto& type : device_ctx.logical_block_types) {
        if (type.pb_graph_head) {
            power_size_pb_rec(type.pb_graph_head);
        }
    }
}

static void power_size_pb_rec(t_pb_graph_node* pb_node) {
    int port_idx, pin_idx;
    int mode_idx, type_idx, pb_idx;
    bool size_buffers_and_wires = true;

    auto& device_ctx = g_vpr_ctx.device();

    auto type = find_most_common_block_type(device_ctx.grid);

    if (!power_method_is_transistor_level(pb_node->pb_type->pb_type_power->estimation_method)
        && pb_node != type->pb_graph_head) {
        /* Area information is only needed for:
         *  1. Transistor-level estimation methods
         *  2. the most common block type for tile size calculations
         */
        return;
    }

    /* Recursive call for all child pb nodes */
    for (mode_idx = 0; mode_idx < pb_node->pb_type->num_modes; mode_idx++) {
        t_mode* mode = &pb_node->pb_type->modes[mode_idx];

        for (type_idx = 0; type_idx < mode->num_pb_type_children; type_idx++) {
            int num_pb = mode->pb_type_children[type_idx].num_pb;

            for (pb_idx = 0; pb_idx < num_pb; pb_idx++) {
                power_size_pb_rec(&pb_node->child_pb_graph_nodes[mode_idx][type_idx][pb_idx]);
            }
        }
    }

    /* Determine # of transistors for this node */
    power_count_transistors_pb_node(pb_node);

    if (pb_node->pb_type->class_type == LUT_CLASS) {
        /* LUTs will have a child node that is used for routing purposes
         * For the routing algorithms it is completely connected; however,
         * this interconnect does not exist in FPGA hardware and should
         * be ignored for power calculations. */
        size_buffers_and_wires = false;
    }

    if (!power_method_is_transistor_level(pb_node->pb_type->pb_type_power->estimation_method)) {
        size_buffers_and_wires = false;
    }

    /* Size all local buffers and wires */
    if (size_buffers_and_wires) {
        for (port_idx = 0; port_idx < pb_node->num_input_ports; port_idx++) {
            for (pin_idx = 0; pin_idx < pb_node->num_input_pins[port_idx];
                 pin_idx++) {
                power_size_pin_buffers_and_wires(&pb_node->input_pins[port_idx][pin_idx], true);
            }
        }

        for (port_idx = 0; port_idx < pb_node->num_output_ports; port_idx++) {
            for (pin_idx = 0; pin_idx < pb_node->num_output_pins[port_idx];
                 pin_idx++) {
                power_size_pin_buffers_and_wires(&pb_node->output_pins[port_idx][pin_idx], false);
            }
        }

        for (port_idx = 0; port_idx < pb_node->num_clock_ports; port_idx++) {
            for (pin_idx = 0; pin_idx < pb_node->num_clock_pins[port_idx];
                 pin_idx++) {
                power_size_pin_buffers_and_wires(&pb_node->clock_pins[port_idx][pin_idx], true);
            }
        }
    }
}

/* Provides statistics about the connection between the pin and interconnect */
static void power_size_pin_to_interconnect(t_interconnect* interc,
                                           int* fanout,
                                           float* wirelength) {
    float this_interc_sidelength;

    /* Pin to interconnect wirelength */
    switch (interc->type) {
        case DIRECT_INTERC:
            *wirelength = 0;
            *fanout = 1;
            break;
        case MUX_INTERC:

        case COMPLETE_INTERC:
            /* The sidelength of this crossbar */
            this_interc_sidelength = sqrt(
                power_transistor_area(
                    interc->interconnect_power->transistor_cnt));

            /* Assume that inputs to the crossbar have a structure like this:
             *
             * A   B|-----
             * -----|---C-
             *      |-----
             *
             * A - wire from pin to point of fanout (grows pb interconnect area)
             * B - fanout wire (sidelength of this crossbar)
             * C - fanouts to crossbar muxes (grows with pb interconnect area)
             */

            *fanout = interc->interconnect_power->num_output_ports;
            *wirelength = this_interc_sidelength;
            //*wirelength = ((1 + *fanout) / 2.0) * power_ctx.arch->local_interc_factor
            //		* pb_interc_sidelength + this_interc_sidelength;
            break;
        default:
            VTR_ASSERT(0);
            break;
    }
}

static void power_size_pin_buffers_and_wires(t_pb_graph_pin* pin,
                                             bool pin_is_an_input) {
    int edge_idx;
    int list_cnt;
    t_interconnect** list;
    bool found;
    int i;

    float C_load;
    float this_pb_length;

    int fanout;
    float wirelength_out = 0;
    float wirelength_in = 0;

    int fanout_tmp = 0;
    float wirelength_tmp = 0;

    float this_pb_interc_sidelength = 0;
    float parent_pb_interc_sidelength = 0;
    bool top_level_pb;

    t_pb_type* this_pb_type = pin->parent_node->pb_type;

    auto& power_ctx = g_vpr_ctx.power();

    /*
     * if (strcmp(pin->parent_node->pb_type->name, "clb") == 0) {
     * //VTR_LOG("here\n");
     * }*/

    this_pb_interc_sidelength = sqrt(
        power_transistor_area(
            pin->parent_node->pb_node_power->transistor_cnt_interc));
    if (pin->is_root_block_pin()) {
        top_level_pb = true;
        parent_pb_interc_sidelength = 0.;
    } else {
        top_level_pb = false;
        parent_pb_interc_sidelength = sqrt(
            power_transistor_area(
                pin->parent_node->parent_pb_graph_node->pb_node_power->transistor_cnt_interc));
    }

    /* Pins are connected to a wire that is connected to:
     * 1. Interconnect structures belonging its pb_node, and/or
     * 		- The pb_node may have one or more modes, each with a set of
     * 		interconnect.  The capacitance is the worst-case capacitance
     * 		between the different modes.
     *
     * 2. Interconnect structures belonging to its parent pb_node
     */

    /* We want to estimate the physical pin fan-out, unfortunately it is not defined in the architecture file.
     * Instead, we know the modes of operation, and the fanout may differ depending on the mode.  We assume
     * that the physical fanout is the max of the connections to the various modes (although in reality it could
     * be higher)*/

    /* Loop through all edges, building a list of interconnect that this pin drives */
    list = nullptr;
    list_cnt = 0;
    for (edge_idx = 0; edge_idx < pin->num_output_edges; edge_idx++) {
        /* Check if its already in the list */
        found = false;
        for (i = 0; i < list_cnt; i++) {
            if (list[i] == pin->output_edges[edge_idx]->interconnect) {
                found = true;
                break;
            }
        }

        if (!found) {
            list_cnt++;
            list = (t_interconnect**)vtr::realloc(list,
                                                  list_cnt * sizeof(t_interconnect*));
            list[list_cnt - 1] = pin->output_edges[edge_idx]->interconnect;
        }
    }

    /* Determine the:
     * 1. Wirelength connected to the pin
     * 2. Fanout of the pin
     */
    if (pin_is_an_input) {
        /* Pin is an input to the PB.
         * Thus, all interconnect it drives belong to the modes of the PB.
         */
        int* fanout_per_mode;
        float* wirelength_out_per_mode;

        fanout_per_mode = (int*)vtr::calloc(this_pb_type->num_modes,
                                            sizeof(int));
        wirelength_out_per_mode = (float*)vtr::calloc(this_pb_type->num_modes,
                                                      sizeof(float));

        for (i = 0; i < list_cnt; i++) {
            int mode_idx = list[i]->parent_mode_index;

            power_size_pin_to_interconnect(list[i], &fanout_tmp,
                                           &wirelength_tmp);

            fanout_per_mode[mode_idx] += fanout_tmp;
            wirelength_out_per_mode[mode_idx] += wirelength_tmp;
        }

        fanout = 0;
        wirelength_out = 0.;

        /* Find worst-case between modes*/
        for (i = 0; i < this_pb_type->num_modes; i++) {
            fanout = std::max(fanout, fanout_per_mode[i]);
            wirelength_out = std::max(wirelength_out, wirelength_out_per_mode[i]);
        }
        if (wirelength_out != 0) {
            wirelength_out += power_ctx.arch->local_interc_factor
                              * this_pb_interc_sidelength;
        }

        free(fanout_per_mode);
        free(wirelength_out_per_mode);

        /* Input wirelength - from parent PB */
        if (!top_level_pb) {
            wirelength_in = power_ctx.arch->local_interc_factor
                            * parent_pb_interc_sidelength;
        }

    } else {
        /* Pin is an output of the PB.
         * Thus, all interconnect it drives belong to the parent PB.
         */
        fanout = 0;
        wirelength_out = 0.;

        if (top_level_pb) {
            /* Outputs of top-level pb should not drive interconnect */
            VTR_ASSERT(list_cnt == 0);
        }

        /* Loop through all interconnect that this pin drives */

        for (i = 0; i < list_cnt; i++) {
            power_size_pin_to_interconnect(list[i], &fanout_tmp,
                                           &wirelength_tmp);
            fanout += fanout_tmp;
            wirelength_out += wirelength_tmp;
        }
        if (wirelength_out != 0) {
            wirelength_out += power_ctx.arch->local_interc_factor
                              * parent_pb_interc_sidelength;
        }

        /* Input wirelength - from this PB */
        wirelength_in = power_ctx.arch->local_interc_factor
                        * this_pb_interc_sidelength;
    }
    free(list);

    /* Wirelength */
    switch (pin->port->port_power->wire_type) {
        case POWER_WIRE_TYPE_IGNORED:
            /* User is ignoring this wirelength */
            pin->pin_power->C_wire = 0.;
            break;
        case POWER_WIRE_TYPE_C:
            pin->pin_power->C_wire = pin->port->port_power->wire.C;
            break;
        case POWER_WIRE_TYPE_ABSOLUTE_LENGTH:
            pin->pin_power->C_wire = pin->port->port_power->wire.absolute_length
                                     * power_ctx.arch->C_wire_local;
            break;
        case POWER_WIRE_TYPE_RELATIVE_LENGTH:
            this_pb_length = sqrt(
                power_transistor_area(
                    power_transistors_for_pb_node(pin->parent_node)));
            pin->pin_power->C_wire = pin->port->port_power->wire.relative_length
                                     * this_pb_length * power_ctx.arch->C_wire_local;
            break;

        case POWER_WIRE_TYPE_AUTO:
            pin->pin_power->C_wire += power_ctx.arch->C_wire_local
                                      * (wirelength_in + wirelength_out);
            break;
        case POWER_WIRE_TYPE_UNDEFINED:
        default:
            VTR_ASSERT(0);
            break;
    }

    /* Buffer */
    switch (pin->port->port_power->buffer_type) {
        case POWER_BUFFER_TYPE_NONE:
            /* User assumes no buffer */
            pin->pin_power->buffer_size = 0.;
            break;
        case POWER_BUFFER_TYPE_ABSOLUTE_SIZE:
            pin->pin_power->buffer_size = pin->port->port_power->buffer_size;
            break;
        case POWER_BUFFER_TYPE_AUTO:
            /* Asume the buffer drives the wire & fanout muxes */
            C_load = pin->pin_power->C_wire
                     + (fanout)*power_ctx.commonly_used->INV_1X_C_in; //power_ctx.commonly_used->NMOS_1X_C_d;
            if (C_load > power_ctx.commonly_used->INV_1X_C_in) {
                pin->pin_power->buffer_size = power_buffer_size_from_logical_effort(C_load);
            } else {
                pin->pin_power->buffer_size = 0.;
            }
            break;
        case POWER_BUFFER_TYPE_UNDEFINED:
        default:
            VTR_ASSERT(0);
    }

    pin->parent_node->pb_node_power->transistor_cnt_buffers += power_count_transistors_buffer(pin->pin_power->buffer_size);
}
